﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using Peter.Common.Controls;
using Peter.Common.Interfaces;
using Peter.Common.Tree;
using Peter.Common.Utilities;

namespace Peter.Data
{
   /// <summary>
   /// Option Data for the file explorer.
   /// </summary>
   [Export (typeof (IOptionData))]
   [PartCreationPolicy (CreationPolicy.Shared)]
   public class FileExplorerOptions : IOptionData, IPartImportsSatisfiedNotification
   {
      private IEnumerable <ExplorerItem> m_Files;
      internal const string FILE_EXPLORER_OPTION_NAME = "Peter.FileExplorer";

      /// <summary>
      /// Gets the name of the option data.
      /// </summary>
      [XmlIgnore]
      public string Name
      {
         get { return FILE_EXPLORER_OPTION_NAME; }
      }

      /// <summary>
      /// Gets the peter data.
      /// </summary>
      [XmlIgnore]
      [Import (typeof (IPeterData))]
      public IPeterData PeterData { get; set; }

      /// <summary>
      /// Gets or Sets the files.
      /// </summary>
      [XmlIgnore]
      public IEnumerable <ExplorerItem> Files
      {
         get { return this.m_Files; }
         set
         {
            this.m_Files = value;
            this.LoadStructure();
         }
      }

      /// <summary>
      /// Gets the expanded nodes.
      /// </summary>
      public string ExpandedNodes { get; set; }

      /// <summary>
      /// Loads the data from xml.
      /// </summary>
      /// <param name="xmlToLoad">Xml string to load.</param>
      public void LoadFromXml (string xmlToLoad)
      {
         FileExplorerOptions loadedData = null;
         try
         {
            loadedData = Serialization.DeserializeFromString<FileExplorerOptions> (xmlToLoad);
         }
         catch (Exception ex)
         {
            Console.Error.Write (ex.Message);
         }

         if (loadedData != null)
            this.CopyPropertiesFrom (loadedData);
         this.LoadStructure ();
      }

      /// <summary>
      /// Loads the expanded structure.
      /// </summary>
      private void LoadStructure ()
      {
         if (string.IsNullOrWhiteSpace (this.ExpandedNodes) || this.Files == null)
            return;
         var data = XDocument.Parse (this.ExpandedNodes);
         if (data.Root != null) {
            foreach (var expanded in data.Root.Elements ()) {
               var item = this.Files.FirstOrDefault (e => e.File.FullName == expanded.Attribute ("path").Value);
               if (item != null) {
                  item.Expanded = expanded;
                  item.IsExpanded = true;
               }
            }
         }
      }

      /// <summary>
      /// Saves any data to xml.
      /// </summary>
      /// <returns>The data to save in Xml format.</returns>
      public string SaveToXml ()
      {
         var expanded = new StringBuilder ();
         using (var writer = XmlWriter.Create(expanded))
            this.SaveStructure (writer);
         var exp = expanded.ToString ();
         exp = exp.Substring (exp.IndexOf ("?>", StringComparison.Ordinal) + 2);
         this.ExpandedNodes = exp;
         return this.Serialize ();
      }

      /// <summary>
      /// Saves the project structure.
      /// </summary>
      /// <param name="writer">Xml output.</param>
      private void SaveStructure (XmlWriter writer)
      {
         writer.WriteStartElement ("structure");
         foreach (var editableTreeViewItem in this.Files.Where (ti => ti.IsExpanded))
         {
            var item = (FileTreeViewItem)editableTreeViewItem;
            SaveExpandedNodes (item, writer);
         }
         writer.WriteEndElement ();
      }

      /// <summary>
      /// Recursively saves the expanded nodes in the project's structure.
      /// </summary>
      /// <param name="treeItem">Tree item to save.</param>
      /// <param name="writer">Xml output.</param>
      private static void SaveExpandedNodes (FileTreeViewItem treeItem, XmlWriter writer)
      {
         writer.WriteStartElement ("expanded");
         writer.WriteAttributeString ("path", treeItem.File.FullName);
         foreach (var editableTreeViewItem in treeItem.Children.Where (ti => ti.IsExpanded))
         {
            var item = (FileTreeViewItem)editableTreeViewItem;
            SaveExpandedNodes (item, writer);
         }
         writer.WriteEndElement ();
      }

      /// <summary>
      /// Called when a part's imports have been satisfied and it is safe to use.
      /// </summary>
      public void OnImportsSatisfied ()
      {
         var type = this.GetType ().ToString ();
         this.LoadFromXml (this.PeterData.RequestData (type));
      }
   }
}
